#include "StdAfx.h"
#include "TaskbarNotifier.h"

/*
	Autor	   : Carmelo Campione - 20 April 2008	
	Base Autor : John O'Byrne - 15 July 2002
*/

namespace cm 
{
	namespace Windows
	{

#define IDT_HIDDEN			0
#define IDT_APPEARING		1
#define IDT_WAITING			2
#define IDT_DISAPPEARING	3
#define TASKBAR_X_TOLERANCE	40
#define TASKBAR_Y_TOLERANCE 40

		inline bool NearlyEqual(int a, int b, int epsilon)
		{
			return abs(a - b) < epsilon / 2;	
		}

		CTaskbarNotifier::CTaskbarNotifier(CWindow* pWndNotify,UINT WMToNotify) 
			: m_pWndNotify(pWndNotify),m_WMToNotify(WMToNotify),
			m_bMouseIsOver(FALSE),
			m_CloseButtonStatus(0),
			m_hBitmapRegion(NULL),
			m_nBitmapHeight(0),m_nBitmapWidth(0),
			m_hCursor(NULL),
			m_crNormalTextColor(RGB(133, 146, 181)),m_crSelectedTextColor(RGB(10, 36, 106)),
			m_dwTimeToStay(0),m_dwShowEvents(0),m_dwHideEvents(0),
			m_nCurrentPosX(0),m_nCurrentPosY(0),m_nCurrentWidth(0),m_nCurrentHeight(0),
			m_nIncrementShow(0),m_nIncrementHide(0),
			m_nTaskbarPlacement(ABE_BOTTOM),m_nAnimStatus(IDT_HIDDEN)

		{
			m_uTextFormat = DT_CENTER | DT_WORDBREAK | DT_END_ELLIPSIS; // Default Text format (see DrawText in the win32 API for the different values)
			m_hCursor = ::LoadCursor(NULL, MAKEINTRESOURCE(32649)); // System Hand cursor

			// If running on NT, timer precision is 10 ms, if not timer precision is 50 ms
			OSVERSIONINFO osvi;
			osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
			GetVersionEx(&osvi);
			if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
				m_dwTimerPrecision = 10;
			else
				m_dwTimerPrecision = 50;

			m_TitleFont.CreatePointFont(80,_T("Arial"));

			SetTextDefaultFont(); // We use default GUI Font
		}

		CTaskbarNotifier::~CTaskbarNotifier(void)
		{
		}

		void CTaskbarNotifier::SetTitle(LPCTSTR Title)
		{
			m_Title = Title;
		}

		void CTaskbarNotifier::Show(LPCTSTR szCaption,DWORD dwTimeToShow,DWORD dwTimeToStay,DWORD dwTimeToHide)
		{
			m_strCaption   = szCaption;
			m_dwTimeToStay = dwTimeToStay;

			UINT nScreenWidth	= ::GetSystemMetrics(SM_CXSCREEN);
			UINT nScreenHeight	= ::GetSystemMetrics(SM_CYSCREEN);

			CRect rcTaskbar;
			HWND hWndTaskbar = ::FindWindow(_T("Shell_TrayWnd"), 0);
			::GetWindowRect(hWndTaskbar,&rcTaskbar);

			// Daniel Lohmann: Calculate taskbar position from its window rect. However, on XP
			// it may be that the taskbar is slightly larger or smaller than the
			// screen size. Therefore we allow some tolerance here.
			UINT nBitmapSize = 0;
			if (NearlyEqual(rcTaskbar.left, 0, TASKBAR_X_TOLERANCE) && NearlyEqual(rcTaskbar.right, nScreenWidth, TASKBAR_X_TOLERANCE))
			{
				// Taskbar is on top or on bottom
				m_nTaskbarPlacement = NearlyEqual(rcTaskbar.top, 0, TASKBAR_Y_TOLERANCE) ? ABE_TOP : ABE_BOTTOM;
				nBitmapSize = m_nBitmapHeight;
			}
			else 
			{
				// Taskbar is on left or on right
				m_nTaskbarPlacement = NearlyEqual(rcTaskbar.left, 0, TASKBAR_X_TOLERANCE) ? ABE_LEFT : ABE_RIGHT;
				nBitmapSize = m_nBitmapWidth;
			}

			// We calculate the pixel increment and the timer value for the showing animation
			if (dwTimeToShow > m_dwTimerPrecision)
			{
				UINT nEvents = min((dwTimeToShow / m_dwTimerPrecision), nBitmapSize);
				m_dwShowEvents = dwTimeToShow / nEvents;
				m_nIncrementShow = nBitmapSize / nEvents;
			}
			else
			{
				m_dwShowEvents = m_dwTimerPrecision;
				m_nIncrementShow = nBitmapSize;
			}

			// We calculate the pixel increment and the timer value for the hiding animation
			if (dwTimeToHide > m_dwTimerPrecision)
			{
				UINT nEvents = min((dwTimeToHide / m_dwTimerPrecision), nBitmapSize);
				m_dwHideEvents = dwTimeToHide / nEvents;
				m_nIncrementHide = nBitmapSize / nEvents;
			}
			else
			{
				m_dwShowEvents = m_dwTimerPrecision;
				m_nIncrementHide = nBitmapSize;
			}

			// Compute init values for the animation
			switch (m_nAnimStatus)
			{
				case IDT_HIDDEN:
				{
					if (m_nTaskbarPlacement == ABE_RIGHT)
					{    
						m_nCurrentPosX = rcTaskbar.left;
						m_nCurrentPosY = rcTaskbar.bottom - m_nBitmapHeight;
						m_nCurrentWidth = 0;
						m_nCurrentHeight = m_nBitmapHeight;
					}
					else if (m_nTaskbarPlacement == ABE_LEFT)
					{
						m_nCurrentPosX = rcTaskbar.right;
						m_nCurrentPosY = rcTaskbar.bottom - m_nBitmapHeight;
						m_nCurrentWidth = 0;
						m_nCurrentHeight = m_nBitmapHeight;
					}
					else if (m_nTaskbarPlacement == ABE_TOP)
					{
						m_nCurrentPosX = rcTaskbar.right - m_nBitmapWidth;
						m_nCurrentPosY = rcTaskbar.bottom;
						m_nCurrentWidth = m_nBitmapWidth;
						m_nCurrentHeight = 0;
					}
					else // if (m_nTaskbarPlacement==ABE_BOTTOM)
					{
						// Taskbar is on the bottom or Invisible
						m_nCurrentPosX = rcTaskbar.right - m_nBitmapWidth;
						m_nCurrentPosY = rcTaskbar.top;
						m_nCurrentWidth = m_nBitmapWidth;
						m_nCurrentHeight = 0;
					}
					ShowWindow(SW_SHOWNOACTIVATE);	
					SetTimer(IDT_APPEARING, m_dwShowEvents, NULL);
					break;
				}
				case IDT_APPEARING:
				{
					RedrawWindow();
					break;
				}
				case IDT_WAITING:
				{
					RedrawWindow();
					KillTimer(IDT_WAITING);
					SetTimer(IDT_WAITING, m_dwTimeToStay, NULL);
					break;
				}
				case IDT_DISAPPEARING:
				{
					KillTimer(IDT_DISAPPEARING);
					SetTimer(IDT_WAITING, m_dwTimeToStay, NULL);
					if (m_nTaskbarPlacement == ABE_RIGHT)
					{
						m_nCurrentPosX = rcTaskbar.left - m_nBitmapWidth;
						m_nCurrentWidth = m_nBitmapWidth;
					}
					else if (m_nTaskbarPlacement == ABE_LEFT)
					{
						m_nCurrentPosX = rcTaskbar.right;
						m_nCurrentWidth = m_nBitmapWidth;
					}
					else if (m_nTaskbarPlacement == ABE_TOP)
					{
						m_nCurrentPosY = rcTaskbar.bottom;
						m_nCurrentHeight = m_nBitmapHeight;
					}
					else // if (m_nTaskbarPlacement==ABE_BOTTOM)
					{
						m_nCurrentPosY = rcTaskbar.top - m_nBitmapHeight;
						m_nCurrentHeight = m_nBitmapHeight;
					}
					SetWindowPos(HWND_TOPMOST, m_nCurrentPosX, m_nCurrentPosY, m_nCurrentWidth, m_nCurrentHeight, SWP_NOACTIVATE);
					RedrawWindow();
					break;
				}
			}
		}

		void CTaskbarNotifier::Hide()
		{
			switch (m_nAnimStatus)
			{
			case IDT_APPEARING:
				KillTimer(IDT_APPEARING);
				break;
			case IDT_WAITING:
				KillTimer(IDT_WAITING);
				break;
			case IDT_DISAPPEARING:
				KillTimer(IDT_DISAPPEARING);
				break;
			}
			MoveWindow(0, 0, 0, 0);
			ShowWindow(SW_HIDE);
			m_nAnimStatus = IDT_HIDDEN;
		}

		HRGN CTaskbarNotifier::CreateRgnFromBitmap(HBITMAP hBmp, COLORREF color)
		{
			// this code is written by Davide Pizzolato

			if (!hBmp)
				return NULL;

			BITMAP bm;
			GetObject(hBmp, sizeof(BITMAP), &bm);	// get bitmap attributes

			CDC dcBmp;
			dcBmp.CreateCompatibleDC(GetDC());	// Creates a memory device context for the bitmap
			dcBmp.SelectBitmap(hBmp);			// selects the bitmap in the device context

			const DWORD RDHDR = sizeof(RGNDATAHEADER);
			const DWORD MAXBUF = 40;		// size of one block in RECTs
			// (i.e. MAXBUF*sizeof(RECT) in bytes)
			LPRECT	pRects;								
			DWORD	cBlocks = 0;			// number of allocated blocks

			INT		i, j;					// current position in mask image
			INT		first = 0;				// left position of current scan line
			// where mask was found
			bool	wasfirst = false;		// set when if mask was found in current scan line
			bool	ismask;					// set when current color is mask color

			// allocate memory for region data
			RGNDATAHEADER* pRgnData =(RGNDATAHEADER*)new BYTE[RDHDR + ++cBlocks * MAXBUF * sizeof(RECT)];
			memset(pRgnData, 0, RDHDR + cBlocks * MAXBUF * sizeof(RECT));
			// fill it by default
			pRgnData->dwSize	= RDHDR;
			pRgnData->iType		= RDH_RECTANGLES;
			pRgnData->nCount	= 0;
			for (i = 0; i < bm.bmHeight; i++)
			{
				for (j = 0; j < bm.bmWidth; j++)
				{
					// get color
					ismask = (dcBmp.GetPixel(j, bm.bmHeight - i - 1) != color);
					// place part of scan line as RECT region if transparent color found after mask color or
					// mask color found at the end of mask image
					if (wasfirst &&((ismask &&(j == (bm.bmWidth - 1))) || (ismask ^(j < bm.bmWidth))))
					{
						// get offset to RECT array if RGNDATA buffer
						pRects =(LPRECT)((LPBYTE)pRgnData + RDHDR);
						// save current RECT
						pRects[pRgnData->nCount++] = CRect(first, bm.bmHeight - i - 1, j + (j == (bm.bmWidth - 1)), bm.bmHeight - i);
						// if buffer full reallocate it
						if (pRgnData->nCount >= cBlocks * MAXBUF)
						{
							LPBYTE pRgnDataNew = new BYTE[RDHDR + ++cBlocks * MAXBUF * sizeof(RECT)];
							memcpy(pRgnDataNew, pRgnData, RDHDR +(cBlocks - 1) * MAXBUF * sizeof(RECT));
							delete pRgnData;
							pRgnData =(RGNDATAHEADER*)pRgnDataNew;
						}
						wasfirst = false;
					} 
					else if (!wasfirst && ismask)
					{
						// set wasfirst when mask is found
						first = j;
						wasfirst = true;
					}
				}
			}
			dcBmp.DeleteDC();	// release the bitmap
			// create region
			/*  Under WinNT the ExtCreateRegion returns NULL(by Fable@aramszu.net) */
			//	HRGN hRgn = ExtCreateRegion( NULL, RDHDR + pRgnData->nCount * sizeof(RECT), (LPRGNDATA)pRgnData );
			/* ExtCreateRegion replacement 
			{
			*/
			HRGN hRgn = CreateRectRgn(0, 0, 0, 0);
			assert(hRgn != NULL);
			pRects =(LPRECT)((LPBYTE)pRgnData + RDHDR);
			for (i = 0; i < (int)pRgnData->nCount; i++)
			{
				HRGN hr = CreateRectRgn(pRects[i].left, pRects[i].top, pRects[i].right, pRects[i].bottom);
				//VERIFY(CombineRgn(hRgn, hRgn, hr, RGN_OR) != ERROR);
				CombineRgn(hRgn, hRgn, hr, RGN_OR);
				if (hr)
					DeleteObject(hr);
			}
			assert(hRgn != NULL);
			/* 
			} ExtCreateRegion replacement */

			delete pRgnData;
			return hRgn;
		}

		BOOL CTaskbarNotifier::SetBitmap(UINT nBitmapID,short red,short green,short blue)
		{
			BITMAP bm;

			if (m_bitmapBackground != NULL)
				m_bitmapBackground.DeleteObject();

			if (!m_bitmapBackground.LoadBitmap(nBitmapID))
				return FALSE;

			GetObject(m_bitmapBackground, sizeof(bm), &bm);
			m_nBitmapWidth	= bm.bmWidth;
			m_nBitmapHeight = bm.bmHeight;
			m_rcText.SetRect(0, 0, bm.bmWidth, bm.bmHeight);
			if (m_CloseButtonBitmap != NULL)
			{
				SIZE sz;
				m_CloseButtonBitmap.GetSize(sz);
				m_CloseButtonBitmapRect.SetRect(m_nBitmapWidth - 2 - sz.cx / 3,2,m_nBitmapWidth - 2, 2 + sz.cy);
				m_rcText.InflateRect(0,-(2 + sz.cy + 2));
			}
			if (red != -1 && green != -1 && blue != -1)
			{
				// No need to delete the HRGN,  SetWindowRgn() owns it after being called

				//m_hBitmapRegion = CreateRgnFromBitmap((HBITMAP)m_bitmapBackground.GetSafeHandle(), RGB(red, green, blue));
				m_hBitmapRegion = CreateRgnFromBitmap(m_bitmapBackground, RGB(red, green, blue));

				SetWindowRgn(m_hBitmapRegion, true);
			}

			return TRUE;
		}

		BOOL CTaskbarNotifier::SetBitmap(LPCTSTR szFileName,short red,short green,short blue)
		{
			BITMAP bm;
			HBITMAP hBmp;

			hBmp = (HBITMAP)::LoadImage(ModuleHelper::GetResourceInstance(), szFileName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
			if (!hBmp)
				return FALSE;

			if (m_bitmapBackground != NULL)
				m_bitmapBackground.DeleteObject();

			m_bitmapBackground.Attach(hBmp);

			GetObject(m_bitmapBackground, sizeof(bm), &bm);
			m_nBitmapWidth	= bm.bmWidth;
			m_nBitmapHeight = bm.bmHeight;
			m_rcText.SetRect(0, 0, bm.bmWidth, bm.bmHeight);
			if (m_CloseButtonBitmap != NULL)
			{
				SIZE sz;
				m_CloseButtonBitmap.GetSize(sz);
				m_CloseButtonBitmapRect.SetRect(m_nBitmapWidth - 2 - sz.cx / 3,2,m_nBitmapWidth - 2, 2 + sz.cy);
				m_rcText.InflateRect(0,-(2 + sz.cy + 2));
			}
			if (red!=-1 && green!=-1 && blue!=-1)
			{
				// No need to delete the HRGN,  SetWindowRgn() owns it after being called

				//m_hBitmapRegion = CreateRgnFromBitmap((HBITMAP)m_bitmapBackground.GetSafeHandle(), RGB(red, green, blue));
				m_hBitmapRegion = CreateRgnFromBitmap(m_bitmapBackground, RGB(red, green, blue));

				SetWindowRgn(m_hBitmapRegion, true);
			}

			return TRUE;
		}

		BOOL CTaskbarNotifier::SetBitmapCloseButton(UINT nBitmapID)
		{
			if (m_CloseButtonBitmap != NULL)
				m_CloseButtonBitmap.DeleteObject();

			if (!m_CloseButtonBitmap.LoadBitmap(nBitmapID))
				return FALSE;

			SIZE sz;
			m_CloseButtonBitmap.GetSize(sz);
			m_CloseButtonBitmapRect.SetRect(m_nBitmapWidth - 2 - sz.cx / 3,2,m_nBitmapWidth - 2, 2 + sz.cy);
			m_rcText.InflateRect(0,-(2 + sz.cy + 2));

			return TRUE;
		}

		void CTaskbarNotifier::SetTextFont(LPCTSTR szFont, int nSize, int nNormalStyle, int nSelectedStyle)
		{
			LOGFONT lf;
			if (m_myNormalFont != NULL)
				m_myNormalFont.DeleteObject();
			m_myNormalFont.CreatePointFont(nSize, szFont);
			m_myNormalFont.GetLogFont(&lf);

			// We  set the Font of the unselected ITEM
			if (nNormalStyle & TN_TEXT_BOLD)
				lf.lfWeight = FW_BOLD;
			else
				lf.lfWeight = FW_NORMAL;

			if (nNormalStyle & TN_TEXT_ITALIC)
				lf.lfItalic = TRUE;
			else
				lf.lfItalic = FALSE;

			if (nNormalStyle & TN_TEXT_UNDERLINE)
				lf.lfUnderline = TRUE;
			else
				lf.lfUnderline = FALSE;

			m_myNormalFont.DeleteObject();
			m_myNormalFont.CreateFontIndirect(&lf);

			// We set the Font of the selected ITEM
			if (nSelectedStyle & TN_TEXT_BOLD)
				lf.lfWeight = FW_BOLD;
			else
				lf.lfWeight = FW_NORMAL;

			if (nSelectedStyle & TN_TEXT_ITALIC)
				lf.lfItalic = TRUE;
			else
				lf.lfItalic = FALSE;

			if (nSelectedStyle & TN_TEXT_UNDERLINE)
				lf.lfUnderline = TRUE;
			else
				lf.lfUnderline = FALSE;

			if (m_mySelectedFont != NULL)
				m_mySelectedFont.DeleteObject();
			m_mySelectedFont.CreateFontIndirect(&lf);
		}

		void CTaskbarNotifier::SetTextDefaultFont()
		{
			LOGFONT lf;
			CFontHandle Font((HFONT)GetStockObject(DEFAULT_GUI_FONT));
			Font.GetLogFont(&lf);
			if (m_myNormalFont != NULL)
				m_myNormalFont.DeleteObject();
			m_myNormalFont.CreateFontIndirect(&lf);

			if (m_mySelectedFont != NULL)
				m_mySelectedFont.DeleteObject();
			lf.lfUnderline = TRUE;
			m_mySelectedFont.CreateFontIndirect(&lf);
		}

		void CTaskbarNotifier::SetTextColor(COLORREF crNormalTextColor, COLORREF crSelectedTextColor)
		{
			m_crNormalTextColor	  = crNormalTextColor;
			m_crSelectedTextColor = crSelectedTextColor;
			RedrawWindow();
		}

		void CTaskbarNotifier::SetTextRect(RECT rcText)
		{
			m_rcText = rcText;
		}

		void CTaskbarNotifier::SetTextFormat(UINT uTextFormat)
		{
			m_uTextFormat = uTextFormat;
		}

		LRESULT CTaskbarNotifier::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
		{
			SetLayeredWindowAttributes(m_hWnd, 0, (255 * 70) / 100, LWA_ALPHA);
			return FALSE;
		}

		LRESULT CTaskbarNotifier::OnMouseMove(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
		{
			TRACKMOUSEEVENT t_MouseEvent;
			t_MouseEvent.cbSize      = sizeof(TRACKMOUSEEVENT);
			t_MouseEvent.dwFlags     = TME_LEAVE | TME_HOVER;
			t_MouseEvent.hwndTrack   = m_hWnd;
			t_MouseEvent.dwHoverTime = 1;

			// We Tell Windows we want to receive WM_MOUSEHOVER and WM_MOUSELEAVE
			::_TrackMouseEvent(&t_MouseEvent);

			BYTE CurrentCloseButtonStatus = 0;// normal;
			
			LONG xPos = GET_X_LPARAM(lParam); 
			LONG yPos = GET_Y_LPARAM(lParam);
			if (m_CloseButtonBitmapRect.PtInRect(CPoint(xPos,yPos)) == TRUE)
			{
				CurrentCloseButtonStatus = 1;//over
				if (wParam & MK_LBUTTON)
				{
					CurrentCloseButtonStatus = 2;//over and pressed
				}
			}
			if (m_CloseButtonStatus != CurrentCloseButtonStatus)
			{
				m_CloseButtonStatus = CurrentCloseButtonStatus;
				RedrawWindow();
			}

			return 0;
		}

		LRESULT CTaskbarNotifier::OnLButtonUp(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
		{
			LONG xPos = GET_X_LPARAM(lParam); 
			LONG yPos = GET_Y_LPARAM(lParam);
			if (m_CloseButtonBitmapRect.PtInRect(CPoint(xPos,yPos)) == TRUE)
			{
				Hide();
				return 0;
			}

			GetParent().PostMessage(m_WMToNotify);

			return 0;
		}

		LRESULT CTaskbarNotifier::OnMouseHover(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
		{
			if (m_bMouseIsOver == FALSE)
			{
				m_bMouseIsOver = TRUE;
				SetWindowLong(GWL_EXSTYLE,GetWindowLong(GWL_EXSTYLE) & ~WS_EX_LAYERED);

				RedrawWindow();
			}

			return 0;
		}

		LRESULT CTaskbarNotifier::OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
		{
			if (m_bMouseIsOver == TRUE)
			{
				m_bMouseIsOver = FALSE;

				m_CloseButtonStatus = 0; // normal;

				SetWindowLong(GWL_EXSTYLE,GetWindowLong(GWL_EXSTYLE) | WS_EX_LAYERED);
				SetLayeredWindowAttributes(m_hWnd,0,(255 * 70) / 100,LWA_ALPHA);

				RedrawWindow();
			}

			return 0;
		}

		LRESULT CTaskbarNotifier::OnEraseBkgnd(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
		{
			CDCHandle		cDC((HDC)wParam);
			CDC				memDC;
			memDC.CreateCompatibleDC(cDC);
			CBitmapHandle OldBitmap = memDC.SelectBitmap(m_bitmapBackground);
			cDC.BitBlt(0, 0, m_nCurrentWidth, m_nCurrentHeight, memDC, 0, 0, SRCCOPY);
			memDC.SelectBitmap(OldBitmap);

			return TRUE;
		}

		LRESULT CTaskbarNotifier::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
		{
			CPaintDC	dc(*this);
			CFontHandle OldFont;

			int OldBk = dc.SetBkMode(TRANSPARENT);

			OldFont = dc.SelectFont(m_TitleFont);

			dc.TextOut(1,1,m_Title,-1);

			dc.SelectFont(OldFont);

			if (m_bMouseIsOver)
			{
				dc.SetTextColor(m_crSelectedTextColor);
				OldFont = dc.SelectFont(m_mySelectedFont);
			}
			else
			{
				dc.SetTextColor(m_crNormalTextColor);
				OldFont = dc.SelectFont(m_myNormalFont);
			}
			
			CRect Dummy(m_rcText);
			Dummy.InflateRect(0,-5);

			dc.DrawText(m_strCaption, -1, Dummy, m_uTextFormat);

			dc.SelectFont(OldFont);

			if (m_CloseButtonBitmap != NULL)
			{
				CDC	memDC;
				memDC.CreateCompatibleDC(dc);
				CBitmapHandle OldBitmap = memDC.SelectBitmap(m_CloseButtonBitmap);
				dc.StretchBlt(m_CloseButtonBitmapRect.left,m_CloseButtonBitmapRect.top,
						m_CloseButtonBitmapRect.Width(),m_CloseButtonBitmapRect.Height(), 
							memDC, 
							m_CloseButtonStatus * m_CloseButtonBitmapRect.Width(), 0, 
								m_CloseButtonBitmapRect.Width(), m_CloseButtonBitmapRect.Height(), 
									SRCCOPY);
				memDC.SelectBitmap(OldBitmap);
			}

			dc.SetBkMode(OldBk);

			return 0;
		}

		LRESULT CTaskbarNotifier::OnSetCursor(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
		{
			UINT nHitTest = LOWORD(lParam);
			if (nHitTest == HTCLIENT)
			{
				SetCursor(m_hCursor);
				return TRUE;
			}

			return FALSE;
		}

		LRESULT CTaskbarNotifier::OnTimer(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
		{
			UINT nIDEvent = wParam;
			switch (nIDEvent)
			{
			case IDT_APPEARING:
				{
					m_nAnimStatus = IDT_APPEARING;
					switch (m_nTaskbarPlacement)
					{
					case ABE_BOTTOM:
						{
							if (m_nCurrentHeight + m_nIncrementShow <= m_nBitmapHeight)
							{
								m_nCurrentPosY -= m_nIncrementShow;
								m_nCurrentHeight += m_nIncrementShow;
							}
							else
							{
								m_nCurrentHeight = m_nBitmapHeight;
								KillTimer(IDT_APPEARING);
								SetTimer(IDT_WAITING, m_dwTimeToStay, NULL);
								m_nAnimStatus = IDT_WAITING;
							}
							break;
						}
					case ABE_TOP:
						{
							if (m_nCurrentHeight + m_nIncrementShow <= m_nBitmapHeight)
							{
								m_nCurrentHeight += m_nIncrementShow;
							}
							else
							{
								m_nCurrentHeight = m_nBitmapHeight;
								KillTimer(IDT_APPEARING);
								SetTimer(IDT_WAITING, m_dwTimeToStay, NULL);
								m_nAnimStatus = IDT_WAITING;
							}
							break;
						}
					case ABE_LEFT:
						{
							if (m_nCurrentWidth + m_nIncrementShow < m_nBitmapWidth)
							{
								m_nCurrentWidth += m_nIncrementShow;
							}
							else
							{
								m_nCurrentWidth = m_nBitmapWidth;
								KillTimer(IDT_APPEARING);
								SetTimer(IDT_WAITING, m_dwTimeToStay, NULL);
								m_nAnimStatus = IDT_WAITING;
							}
							break;
						}
					case ABE_RIGHT:
						{
							if (m_nCurrentWidth + m_nIncrementShow <= m_nBitmapWidth)
							{
								m_nCurrentPosX -= m_nIncrementShow;
								m_nCurrentWidth += m_nIncrementShow;
							}
							else
							{
								m_nCurrentWidth = m_nBitmapWidth;
								KillTimer(IDT_APPEARING);
								SetTimer(IDT_WAITING, m_dwTimeToStay, NULL);
								m_nAnimStatus = IDT_WAITING;
							}
							break;
						}
					}
					SetWindowPos(HWND_TOPMOST, m_nCurrentPosX, m_nCurrentPosY, m_nCurrentWidth, m_nCurrentHeight, SWP_NOACTIVATE);
					break;
				}
			case IDT_WAITING:
				{
					if (m_bMouseIsOver == FALSE)
					{
						KillTimer(IDT_WAITING);
						SetTimer(IDT_DISAPPEARING, m_dwHideEvents, NULL);
					}
					break;
				}
			case IDT_DISAPPEARING:
				{
					m_nAnimStatus = IDT_DISAPPEARING;
					switch (m_nTaskbarPlacement)
					{
					case ABE_BOTTOM:
						{
							if (m_nCurrentHeight>0)
							{
								m_nCurrentPosY += m_nIncrementHide;
								m_nCurrentHeight -= m_nIncrementHide;
							}
							else
							{
								KillTimer(IDT_DISAPPEARING);
								Hide();
							}
							break;
						}
					case ABE_TOP:
						{
							if (m_nCurrentHeight>0)
							{
								m_nCurrentHeight -= m_nIncrementHide;
							}
							else
							{
								KillTimer(IDT_DISAPPEARING);
								Hide();
							}
							break;
						}
					case ABE_LEFT:
						{
							if (m_nCurrentWidth>0)
							{
								m_nCurrentWidth -= m_nIncrementHide;
							}
							else
							{
								KillTimer(IDT_DISAPPEARING);
								Hide();
							}
							break;
						}
					case ABE_RIGHT:
						{
							if (m_nCurrentWidth>0)
							{
								m_nCurrentPosX += m_nIncrementHide;
								m_nCurrentWidth -= m_nIncrementHide;
							}
							else
							{
								KillTimer(IDT_DISAPPEARING);
								Hide();
							}
							break;
						}
					}
					SetWindowPos(HWND_TOPMOST, m_nCurrentPosX, m_nCurrentPosY, m_nCurrentWidth, m_nCurrentHeight, SWP_NOACTIVATE);
					break;
				}
			}
			return 0;
		}
	}
}
